/**********************************************************************
 Copyright (C) 2011 Jaakko Vuori

 This file is part of ShredMate.
 For more information, see <http://code.google.com/p/shredmate/>

 ShredMate is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 ShredMate is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with ShredMate.  If not, see <http://www.gnu.org/licenses/>.
 **********************************************************************/

#include "shredmateengine.h"

#include <QAudioOutput>

#include "abstractpcmbuffer.h"
#include "pcmgenerator.h"

#include <QDebug>

ShredmateEngine::ShredmateEngine(QObject* parent) : QObject(parent),
    m_pcmBuffer(NULL), m_pcmGenerator(new PCMGenerator(this)), m_audioOutput(NULL), m_activeRange(-1), m_currentPosition(0)
{
    connect(m_pcmGenerator, SIGNAL(ended()), SLOT(stop()));
}

ShredmateEngine::~ShredmateEngine()
{
}

void ShredmateEngine::setPCMBuffer(const AbstractPCMBuffer* pcmBuffer)
{
    delete m_audioOutput;
    m_audioOutput = NULL;

    m_pcmBuffer = pcmBuffer;

    if (!m_pcmBuffer)
        return;

    m_pcmGenerator->setData(pcmBuffer->data(), pcmBuffer->length());
    setPosition(0);

    QAudioDeviceInfo info = QAudioDeviceInfo::defaultOutputDevice();
    qDebug() << "info.deviceName() = " << info.deviceName();
    qDebug() << "pcmBuffer->bitRate()" << pcmBuffer->bitRate();
    qDebug() << "pcmBuffer->channels()" << pcmBuffer->channels();

    QAudioFormat format;
    format.setFrequency(pcmBuffer->bitRate());
    format.setChannels(pcmBuffer->channels());
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    if (!info.isFormatSupported(format))
    {
        qWarning() << "raw audio format not supported by backend, cannot play audio.";
        throw;
    }

    m_audioOutput = new QAudioOutput(format);
    m_audioOutput->setNotifyInterval(10);
    connect(m_audioOutput, SIGNAL(notify()), SIGNAL(notify()));
    connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State)));
    m_audioOutput->start(m_pcmGenerator);
    m_audioOutput->suspend();
}

void ShredmateEngine::handleStateChanged(QAudio::State state)
{
    qDebug() << "handleStateChanged" << state;
}

QList<Marker> ShredmateEngine::markers() const
{
    return m_markers;
}

void ShredmateEngine::setMarkers(QList<Marker> markers)
{
    m_markers = markers;
}

QList<Range> ShredmateEngine::ranges() const
{
    return m_ranges;
}

void ShredmateEngine::setRanges(QList<Range> ranges)
{
    Range previousActiveRange;
    if (m_activeRange != -1)
        previousActiveRange = m_ranges[m_activeRange];
    m_ranges = ranges;
    setActiveRange(m_ranges.indexOf(previousActiveRange));
}

void ShredmateEngine::setActiveRange(int index)
{
    m_activeRange = index;
    if (index != -1)
    {
        Range range = m_ranges[m_activeRange];
        if (m_pcmBuffer)
            m_pcmGenerator->setData(m_pcmBuffer->data() + range.lowerMarker().position() / 2 * 2, range.length());
    }
    else
    {
        if (m_pcmBuffer)
            m_pcmGenerator->setData(m_pcmBuffer->data(), m_pcmBuffer->length());
    }
}

int ShredmateEngine::activeRange() const
{
    return m_activeRange;
}

void ShredmateEngine::play()
{
    qDebug() << "ShredmateEngine::play()";
    if (m_audioOutput)
        m_audioOutput->resume();
}

void ShredmateEngine::stop()
{
    qDebug() << "ShredmateEngine::stop()";
    if (m_audioOutput)
        m_audioOutput->suspend();
}

void ShredmateEngine::togglePause()
{
    qDebug() << "ShredmateEngine::togglePause()";
    if (m_audioOutput)
    {
        if (m_audioOutput->state() == QAudio::SuspendedState)
            m_audioOutput->resume();
        else
            m_audioOutput->suspend();
    }
}

bool ShredmateEngine::isPlaying() const
{
    if (m_audioOutput)
        return m_audioOutput->state() != QAudio::SuspendedState;
    return false;
}

void ShredmateEngine::setPosition(qint64 position)
{
    if (m_activeRange != -1)
    {
        int newPosition = position - m_ranges[m_activeRange].lowerMarker().position();
        //qDebug() << "newPosition" << newPosition << "m_pcmBuffer->length()" << m_pcmGenerator->bufferLength();
        if (newPosition < 0 || newPosition > m_pcmGenerator->bufferLength() - 1)
        {
            setActiveRange(-1);
            m_pcmGenerator->m_currentPosition = position;
        }
        else
            m_pcmGenerator->m_currentPosition = newPosition;
    }
    else
        m_pcmGenerator->m_currentPosition = position;
}

qint64 ShredmateEngine::position() const
{
    if (m_activeRange != -1)
        return m_pcmGenerator->m_currentPosition + m_ranges[m_activeRange].lowerMarker().position();
    return m_pcmGenerator->m_currentPosition;
}

qint64 ShredmateEngine::bufferLength() const
{
    if (m_pcmBuffer)
        return m_pcmBuffer->length();
    return -1;
}

void ShredmateEngine::setLooping(bool looping)
{
    m_pcmGenerator->m_looping = looping;
}

bool ShredmateEngine::isLooping() const
{
    return m_pcmGenerator->m_looping;
}
